﻿using Niknak.Services.ServiceCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Diagnostics.Eventing.Reader;

namespace Sandbox.Business
{
    public class ExceptionHandler : IExceptionHandler
    {
        #region Constants that would normally be config

        private const string EVENTLOGSOURCE = "Sandbox Console";

        #endregion

        public string CustomEventLogName { get; set; }

        public ExceptionHandler()
        {
            this.CustomEventLogName = "Niknak Services";
        }

        public void ExceptionLogEntry(Exception ex)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("Exception: " + ex.Message);
            Console.ResetColor();

            StringBuilder sb = new StringBuilder();
            sb.AppendLine("Exception information:");
            sb.AppendLine("\tException type: " + ex.GetType().ToString());
            sb.AppendLine("\tException message: " + ex.Message);
            sb.AppendLine("\tException stack trace: " + ex.StackTrace);

            Exception inner = ex.InnerException;
            while (inner != null)
            {
                sb.AppendLine("\tInner exception message: " + inner.Message);
                sb.AppendLine("\tInner exception stack trace: " + inner.StackTrace);
                inner = inner.InnerException;
            }

            sb.AppendLine();

            WriteToEventLog(sb.ToString(), EventLogEntryType.Error, 2000);
        }

        public void WriteToEventLog(string message, EventLogEntryType entryType, int eventId)
        {
            try
            {
                EnsureEventLog();

                Trace.TraceInformation("Writing to event log");
                EventLog.WriteEntry(EVENTLOGSOURCE,
                    message.ToString(),
                    entryType,
                    eventId
                );
            }
            catch (Exception ex)
            {
                //nothing much we can do about it now
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Exception: " + ex.Message);
                Console.ResetColor();
            }
        }

        private IEnumerable<EventRecord> QueryEventRecords(string logName, int[] levels, string providerName, DateTime afterTimestamp, DateTime beforeTimestamp)
        {
            string queryString = String.Format(
                "*[System[({0}){1} and TimeCreated[@SystemTime >= '{2}'] and TimeCreated[@SystemTime <= '{3}']]]",
                string.Join(" or ", levels.Select(l => "Level=" + l)),
                !string.IsNullOrEmpty(providerName) ? " and Provider[@Name = '" + providerName + "'" : "",
                afterTimestamp.ToUniversalTime().ToString("o"),
                beforeTimestamp.ToUniversalTime().ToString("o")
            );

            EventLogQuery query = new EventLogQuery(logName, PathType.LogName, queryString);
            query.ReverseDirection = true;

            using (var reader = new EventLogReader(query))
            {
                EventRecord eventRecord;
                while ((eventRecord = reader.ReadEvent()) != null)
                    yield return eventRecord;
            }
        }

        public EventRecord[] ReadFromEventLog(string logName, int[] logLevels, string providerName, DateTime afterTimestamp, DateTime beforeTimestamp, int skip, int take)
        {
            try
            {
                return QueryEventRecords(logName, logLevels, providerName, afterTimestamp, beforeTimestamp).Skip(skip).Take(take).ToList().ToArray();
            }
            catch (Exception ex)
            {
                //nothing much we can do about it
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Exception: " + ex.Message);
                Console.ResetColor();
            }

            return null;
        }


        private void EnsureEventLog()
        {

            if (!EventLog.GetEventLogs().Where(ev => ev.LogDisplayName.Equals(CustomEventLogName)).Any())
            {
                Trace.TraceInformation("Ensuring event log");

                //no event log at all - but there is a chance there is a source, so remove the source to avoid exception
                if (EventLog.SourceExists(EVENTLOGSOURCE))
                    EventLog.DeleteEventSource(EVENTLOGSOURCE);

                EventLog.CreateEventSource(EVENTLOGSOURCE, CustomEventLogName);
                return;
            }

            if (!EventLog.SourceExists(EVENTLOGSOURCE))
            {
                Trace.TraceInformation("Ensuring event log source");

                EventLog.CreateEventSource(EVENTLOGSOURCE, CustomEventLogName);
            }
        }
    }
}
